// Example3.java

package com.ibm.tspaces.examples.simple;

import com.ibm.tspaces.*;
import java.util.Vector;
//import com.ibm.Tspaces.server.*;


/*
**  Licensed Materials - Property of IBM
**
**  (C) COPYRIGHT IBM Corp. 1996, 1997  All rights reserved
**
**  US Government Users Restricted Rights - Use, duplication or
**  disclosure restricted by GSA ADP Schedule Contract with IBM Corp.
**
**/

/**
** Show how a client can register for events that are happening to a tuple space.
**
** This class will implement the Callback interface which requires that 
** it have a "call" method.   An instance of this class will be specified as 
** a parameter to "eventRegister".  Then when the event that we are waiting for 
** occurs, the call method for the object will be given control.
** <pre>
** This example has 3 important Threads 
**   - main    Thread that is started for the main method and also has control
**             when the test() method is called.
**             It will delete the Tuples that are written to the space.
**
**   - run     This is the Thread that is started by the main() method and uses 
**             the run() method to implement the thread.  
**             This thread will write tuples to the space.  Normally this would 
**             done by another application.
**   
**   - callback This is the callback thread.  This thread is started when the 
**             TupleSpace is initially created and it handles the information 
**             that is returned from TupleSpace commands.  It also is used 
**             to implement Event Callbacks by invoking the call() method.
**             Note that the user does not create this thread.  
** </pre>
** This example how to register for TupleSpace events and how to correctly 
** pass the tuples from the callback to other threads that will process them.
**
** Note that there is another way to handle this.  One can specify in the
** eventRegister() method (newThread=true) that a new Thread should be created 
** for each callback.  Then the Tuples can be deleted in the callback.  
** The only disadvantage with this is that each and every callback will 
** create a new thread which is very high overhead if there are lots of callbacks.
**
**             
** @see Callback
** @see Waiter
** @see TupleSpace
**
** @author John Thomas
** @version $Revision: 2.1.2.4 $ $Date: 2000/09/08 18:44:09 $
*************************************************************************
*/

public class Example3 implements Callback, Runnable  {

private TupleSpace 	ts = null;

private static String 		host = "localhost";
private	int			port	=   TupleSpace.DEFAULTPORT;
private boolean _event  = false;
private boolean _quit   = false;	
/**
** Simple Queue 
*/ 
private Queue  _queue = new Queue(20);
	
  /*
  ***************************************************************************
  ** ** constructor    **
  ** ********************
  */
  /**
  ** The constructor for this class will setup everything so that the 
  ** call method will be given control when interesting things happen 
  ** to the related TupleSpace
  */
public 
Example3() { 
	int seqNum;
	try { 
		
		// Verify that the Server is active

	 	Tuple active = TupleSpace.status(host,port);
	    // status() returns a simple Tuple that contains "Running" or "NotRunning"
		Debug.out("Status="+active);
		if ( active == null || active.getField(0).getValue().equals("NotRunning")) { 
			Debug.out("TSpaces server not running on " + 
						host+":"+port );
		    System.exit(1);
		}
	
		// access the TupleSpace
		ts = new TupleSpace("Example3",host,port);
	 	//ts.setDebug(true);
		// Setup a template to describe the Tuples we are interested in.
		Tuple template = new Tuple( "Key2", new Field(String.class) );
		
    // set flag for whether a new Thread will process the callback
    boolean newThread = false;    // default is false
		// register for the write event 
		// Indicate that we ourselves are implementing the Callback interface
		seqNum  = ts.eventRegister(	TupleSpace.WRITE, template, this, newThread);
		
		Debug.out("Event Registration. seqnum="+seqNum);
	} catch (TupleSpaceException tse) {
		Debug.out(tse);
	}

} // Example3() constructor

	
/**
** This run method will write some Tuples to the space 
** This could represent the actions of another client.
**
*/

public void 
run() {
	
  try {
    Debug.out("Write Key1");
    // no event for this one!
    ts.write( "Key1","data1" );
  	
    Debug.out("Write Key2");    
    // an event for this one!
    ts.write( "Key2", "data2" );
  	
  	Debug.out("Write Key3");    
    ts.write( "Key3", "data3" );
  	
    Debug.out("Write Key2"); 
  	// an event for this one!
    ts.write( "Key2", "data2again" );
  	
    // an event to cause it to quit
  	Debug.out("Write Quit"); 
  	ts.write( "Key2","Quit");
  } catch (Exception e) {
    Debug.out(e);
    
  }
}

/**
** This represents the main application processing.
**
** It waits for a Tuple to appear in  a Vector 
** that represents a queue of 
** work to be done.  If there is a Tuple in the queue
** it will remove it from the queue and process it.
** In this case, it just deletes the tuples.  
** When we get a tuple that has "quit" as the 2nd field 
** then we clean up and leave.
**
** We could write a better queueing facility but this is 
** supposed to be a simple example.
**
**  
*/

public void
test() {
  try {
    
    Tuple myTuple = null; 
	  
    String data = "";
    
    while (! _quit) {
      // when a tuple arrives that we are interested in
      // the call rtn will put it in the _queue vector 
      
      myTuple = (Tuple)_queue.get();
      Debug.out("Got an event "+myTuple);
      // Let's delete the Tuple after getting it in the callback.
      // Note tht we can not do this in the callback method 
      // code.  It would result in a never ending wait.
      TupleID id = myTuple.getTupleID();            
      
      data = (String)myTuple.getField(1).getValue();
      try {
        Debug.out("Take tuple "+id);
        Tuple taken = (Tuple)ts.takeTupleById(id);
        Debug.out("taken="+taken);
        if (taken == null) 
          Debug.out("Somebody else may have deleted tuple. "+ id);
      } catch (TupleSpaceException tse) {
        Debug.out(tse.getMessage());        
      }
      
      if (data.equals("Quit") ) {
        _quit = true;
        
      }
       
    }
    ts.cleanup();
    
		
	} catch (TupleSpaceException tse) {
		Debug.out(tse);
		tse.printStackTrace();
	} catch (Exception e) {
	  Debug.out( e);
  }
}


  /*
  ***************************************************************************
  ** ** call    **
  ** *************
  */
  /**
  ** Implementation for the Callback interface.
  **
  ** call an application back with a tuple it requested. In the case of an exceptional condition
  ** occurring on the server, isException will be true and the exception will be inside the tuple_,
  ** i.e., the tuple will contain one field that is a TupleSpaceServerException.
  ** <p>
  ** EventRegister has an otional parameter that controls whether the 
  ** callback is processed in the same thread that reads the response 
  ** from server or whether a new Thread is started to process the callback.
  ** <p>
  ** The default is to use the same thread and simply callthe callback thread.
  ** Thus in this case the callback should be really quick and
  ** return, that is it should not take up lots of time. 
  ** Typically, it might just check that an exception was not thrown and if not
  ** then queue  up the tuple and wake up some other thread that will do the real processing,
  ** Likewise in default case, Because this method is running in a thread that 
  ** reports all callbacks from the server,
  ** it is important that the call() method (or any method that it invokes directly)
  ** do <b>not</b> invoke another TupleSpace command.  This would cause the communication 
  ** to the TSpaces Server to hang.
  ** <p> 
  ** However if eventRegister(cmd,template,callback,<b>true</b>) is specified then
  ** the callback will be in a new thread and there are no restrictions.
  **
  ** Also, normally the call() method will return false. It would return true 
  ** only if this is the last call this callback class is expecting.
  ** For a one time read or in etc this is the case, but for EventHandlers, this is not
  ** the case, they will keep on servicing more and more events. 
  ** 
  ** @param eventName_ String that indicates what type of TupleSpace action 
  ** "Write" or "Delete" caused this call.  
  ** Note that only events that a cause a Write or Delete to be done are
  ** reported.  A READ operation at the server is not reported.
  ** @param tsName_ the name of the tuple space this command was executed on.
  ** @param sequenceNumber_ the sequenceNumber for this event/command
  ** @param tuple_ the returned tuple or a Tuple with an exception inside
  ** @param isException_ was the command processed normaly or was there an exception
  ** 
  ** @return return false normally, true if this is the last call
  **                this method should be getting.
	** @see TupleSpace
	** @see CallbackThread
	
	*/



public boolean 
call(	String eventName,String tsName,int seqNum,SuperTuple tuple,boolean isException) 	{
	try {						
		// do what you want here, probably wake up some other
		// thread and queue up the info for it.
		// Remember, this code is being run in the thread
		// that handles callbacks from the server so don't issue TupleSpace commands.
		Debug.out("in call for " + eventName 
				 + ", TupleSpace = " + tsName  
				 + ",Seq #=" + seqNum + ",Tuple=" 
				 + tuple + ", isException=" + isException);
	  
		// If no exception at the server, then set a switch and 
		// place the tuple in a queue.
		if (! isException) {      
      
        _queue.put(tuple);    // add Tuple to queue        
        Debug.out("Queueed "+tuple);  
        
  
		} else  {     // here if server exception
     Exception e = (Exception)tuple.getField(0).getValue();
     if (e.getMessage().equals("Normal Close")) {
       Debug.out(0,"Normal close event from server");
     } else {
       Debug.out(0,e);
     }
	   
     _quit = true; //
     }
		
	
	
	} catch (Exception e) {
		Debug.out(e);
	
	}

	return false;
} // call()
		
	/**
	** The main method for this class
	**
	*/	

public static void 
main(String[] argv) {
  //Debug.setDebugOn(true);
  //TupleSpace.setDebug(true);

	try {
    
    if (argv.length > 0) 
      host = argv[0];      // if user specified server host
    if ((argv.length > 1) && (argv[1].equals("-D")) ) {
      Debug.setDebugOn(true);
      //TupleSpace.setDebug(true);
    }

		

	  // create an instance of the class
    // This will create the space 
    // it will register for any write events on the space.
    //   The resulting callback will queue the tuples.
		Example3 instance = new Example3();
    
		// startup the run() method to write some tuples
		Thread t = new Thread(instance,"run");
		t.start();
	
		// run the test() method which read the tuples
    // from the queue and delete them.
		instance.test();	
	
		Debug.out(0,"Example3 was successful.");
		//Thread.sleep(5000);
	  System.exit(0);
	} catch(Exception e) {
		Debug.out(0,"Caught an exception: ",e);
		
	}
} // end main


} // Example3.java
/* $Log: Example3.java,v $
/* Revision 2.1.2.4  2000/09/08 18:44:09  jthomas
/* no message
/*
/* Revision 2.1.2.3  2000/08/30 12:57:09  jthomas
/* update to use Queue class
/*
/* Revision 2.1.2.2  2000/06/28 16:02:22  jthomas
/* revise the example so it won't hang because of bad queue handling
/*
/* Revision 2.1.2.1  2000/02/10 14:19:48  jthomas
/* Fix testcase so it doesn't hang.
/*
 */
